﻿using PropertyChanged;
using SearchInFiles.Global;
using SearchInFiles.Lib.Tools;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SearchInFiles.Lib.Search
{
    [AddINotifyPropertyChangedInterface]public record class SearchResult:INotifyPropertyChanged
    {
        public static readonly SearchResult Empty = new SearchResult(-1, string.Empty, string.Empty, null!, null);

        public event PropertyChangedEventHandler? PropertyChanged;
        public FileElements.FileElement FileElement { get; private set; }
        public int SourceHitPosition { get;private set; }
        public string? SourceInfo { get; private set; }
        public string SourceFilePath => FileElement.Path;
        public string SourceFullInfo => $"{SourceFilePath}{(string.IsNullOrEmpty(SourceInfo) ? $"->{SourceInfo}" : string.Empty)}";
        public string PartialContext { get; private set; }
        public int PartialContextHitStartPosition { get; private set; }
        public int PartialContextHitEndPosition { get; private set; }

        [DependsOn(nameof(PartialContext), nameof(PartialContextHitStartPosition), nameof(PartialContextHitEndPosition))]
        public string HitString => PartialContext[PartialContextHitStartPosition..PartialContextHitEndPosition];
        [DependsOn(nameof(PartialContextHitStartPosition), nameof(PartialContextHitEndPosition))]
        public int HitStringLength => PartialContextHitEndPosition - PartialContextHitStartPosition;
        public bool IsSelected { get; set; }
        
        public SearchResult(int SourceHitPosition, string hit, string partialContext, FileElements.FileElement FileElement, string? sourceInfo)
        {
            this.SourceHitPosition = SourceHitPosition;
            PartialContext = partialContext;
            PartialContextHitStartPosition = partialContext.IndexOf(hit);
            PartialContextHitEndPosition = PartialContextHitStartPosition + hit.Length;
            this.FileElement = FileElement;
            SourceInfo = sourceInfo;
            IsSelected = false;
        }
        public static async Task ReplaceModify(IEnumerable<SearchResult> searchResults, string ReplaceString, CancellationTokenSource cancellationTokenSource, Action<int>? Progress = null) => await Task.Run(() =>
        {
            Dictionary<FileElements.FileElement, SearchResult[]> dic = searchResults.GroupBy(p => p.FileElement).ToDictionary(k => k.Key, v => v.ToArray());
            int max = searchResults.Count();
            int count = 0;
            UtilityTools.SetParallelMode(dic, cancellationTokenSource, 5, 8).AsUnordered().ForAll(p =>
            {
                StringBuilder FileText = new StringBuilder(File.ReadAllText(p.Key.Path, p.Key.encodingData.Value));
                int offSet = 0;
                int CurrentStart, CurrentEnd, PartialContextHitLength, ReplaceEnd;

                foreach (SearchResult result in p.Value.OrderBy(sr => sr.SourceHitPosition))
                {
                    //计算出当前项的真实位置-同一个字符串，每次改动，都会使后面的字符串整体位置产生变化
                    //因此要重新计算结果项中记录的的位置
                    //位置的变化每次都写到offset中，每个项根据上一次计算后的offset重新计算
                    PartialContextHitLength = result.HitStringLength;
                    CurrentStart = result.SourceHitPosition + offSet;
                    CurrentEnd = result.SourceHitPosition + PartialContextHitLength + offSet;
                    ReplaceEnd = CurrentStart + ReplaceString.Length;

                    result.PartialContext = result.PartialContext.Replace(result.HitString, ReplaceString, System.StringComparison.OrdinalIgnoreCase);
                    FileText.Remove(CurrentStart, PartialContextHitLength).Insert(CurrentStart, ReplaceString);

                    offSet += ReplaceEnd - CurrentEnd;
                    result.SourceHitPosition = CurrentStart;
                    result.PartialContextHitEndPosition = result.PartialContextHitStartPosition + ReplaceString.Length;
                    Progress?.Invoke(count / max);
                    Interlocked.Increment(ref count);
                }
                File.WriteAllText(p.Key.Path, FileText.ToString(), p.Key.encodingData.Value);
            });
        }).ConfigureAwait(false);
        public static string GetPartialContext(string Source, string HitString, int HitPosition, int FrontLength, int AfterLength)
        {
            //求扩展到前面的位置值
            FrontLength = HitPosition - FrontLength;
            //求扩展到后面的值
            AfterLength = HitPosition + HitString.Length + AfterLength;
            //前面的位置值最小限制到0
            if (FrontLength < 0)
                FrontLength = 0;
            //后面的值最大限制到结尾
            if (AfterLength > Source.Length)
                AfterLength = Source.Length;
            return Source[FrontLength..AfterLength];
        }
    }
}
